<?php

/**
 * Description of Reranking
 *
 * @author Pavel Homolka
 */
use Nette\Utils\Arrays;

class Reranking {

    private $session;

    public function __construct(PhotosSession $photosSession) {
        $this->session = $photosSession;
    }

    /**
     * Pro atribut obrazku ulozeny v session a zadanou hodnotu tohoto atributu 
     * setridi pole podle editacni vzdalenosti.
     * 
     * @param string $photoAtribut Atribut obrazku podle session
     * @param type $value Hodnota artibutu zadana uzivatelem.
     */
    public function levenshteinDistance($photoAtribut, $value, $radius) {
        $photos = $this->session->getResultingPhotos();
        $this->session->setMinLevenshteinDistance(99999999);

        for ($i = 0; $i < count($photos); $i++) {

            if (!empty($photos[$i][$photoAtribut])) {
                //vraci aktualni stav, pokud neexistuje vraci true
                $state = Arrays::get($photos[$i], 'state', 'true');

                if ($state) {
                    $words = explode(' ', $photos[$i][$photoAtribut]);
                    foreach ($words as $word) {
                        $distance[$word] = levenshtein($word, $value);
                    }
                    $photos[$i]['levenshtein' . '_' . $photoAtribut] = min($distance);
                    min($distance) <= $radius ? $photos[$i]['state'] = "true" : $photos[$i]['state'] = "false";

                    if ($this->session->getMinLevenshteinDistance() >= min($distance))
                        $this->session->setMinLevenshteinDistance(min($distance));

                    unset($distance);
                } else {
                    $photos[$i]['state'] = "false";
                }
            } else {
                $photos[$i]['state'] = "false";
            }
        }


        $photos = $this->sortPhotos($photos, 'levenshtein' . '_' . $photoAtribut);
        $this->session->clear();
        $this->session->add($photos);
    }

    /**
     * Serazeni fotek z session podle vzdalenosti od zadaneho bodu.
     * 
     * @param double $latitude Zeměpisná šířka.
     * @param double $longitude Zeměpisná délka.
     */
    public function greatCircleDistance($latitude, $longitude, $radius) {
        $photos = $this->session->getResultingPhotos();
        $this->session->setMinGreatCircleDistance(999999);
        for ($i = 0; $i < count($photos); $i++) {
            if (!empty($photos[$i]['latitude']) || !empty($photos[$i]['longitude'])) {

                //vraci aktualni stav, pokud neexistuje vraci true
                $state = Arrays::get($photos[$i], 'state', 'true');
                if ($state) {
                    $point1['latitude'] = $latitude;
                    $point1['longitude'] = $longitude;

                    $point2['latitude'] = $photos[$i]['latitude'];
                    $point2['longitude'] = $photos[$i]['longitude'];

                    $distance = $this->calculateDistanceFromLatLong($point1, $point2);
                    $photos[$i]['greatCircleDistance'] = $distance;
                    $distance <= $radius ? $photos[$i]['state'] = "true" : $photos[$i]['state'] = "false";
                    if ($this->session->getMinGreatCircleDistance() >= $distance)
                        $this->session->setMinGreatCircleDistance($distance);
                } else {
                    $photos[$i]['state'] = "false";
                }
            } else {
                $photos[$i]['state'] = "false";
            }
        }

        $photos = $this->sortPhotos($photos, 'greatCircleDistance');
        $this->session->clear();
        $this->session->add($photos);
    }

    /**
     * Setrideni fotek podle datumu
     * 
     * @param string $typeDate dateupload nebo datetaken
     * @param string $date Zadane datum ve formatu "YYYY-MM-DD"
     * @param int $radius Citlivost
     */
    public function dateDistance($typeDate, $date, $radius) {
        $photos = $this->session->getResultingPhotos();

        for ($i = 0; $i < count($photos); $i++) {
            if (!empty($photos[$i][$typeDate])) {
                //vraci aktualni stav, pokud neexistuje vraci true
                $state = Arrays::get($photos[$i], 'state', 'true');
                if ($state) {

                    if ($typeDate == 'datetaken') {
                        $distance = $this->dateDiff(date("Y-m-d", strtotime($photos[$i][$typeDate])), $date);
                    } else {
                        $distance = $this->dateDiff(date("Y-m-d",$photos[$i][$typeDate]), $date);
                    }

                    $distance < 0 ? $distance = $distance * -1 : $distance = $distance;
                    $photos[$i]['dateDistance'] = $distance;
                    $distance <= $radius ? $photos[$i]['state'] = "true" : $photos[$i]['state'] = "false";
                } else {
                    $photos[$i]['state'] = "false";
                }
            } else {
                $photos[$i]['state'] = "false";
            }
        }

        $photos = $this->sortPhotos($photos, 'dateDistance');
        $this->session->clear();
        $this->session->add($photos);
    }

    /**
     * Serazeni pole fotek podle argumentu.
     * 
     * @param Array $photos Pole fotek, kazda fotka obsahuje jako index argument pro razeni.
     * @param String $arg Argumen pro razeni.
     * @return Array Serazene pole.
     */
    private function sortPhotos($photos, $arg) {

        $trueArray = array();
        $falseArray = array();

        foreach ($photos as $photo) {
            if ($photo['state'] == 'true')
                $trueArray[] = $photo;

            if ($photo['state'] == 'false')
                $falseArray[] = $photo;
        }

        if (!empty($trueArray)) {

            foreach ($trueArray as $photo) {
                $sortArray[] = $photo[$arg];
            }

            array_multisort($sortArray, $trueArray);

            foreach ($falseArray as $photo) {
                $trueArray[] = $photo;
            }

            return $trueArray;
        } else {
            return $falseArray;
        }
    }

    /**
     * Vypocet vzdalenosti
     * 
     * @param Array $point1 Souradnice bodu 1.
     * @param Array $point2 Souradnice bodu 2.
     * @param String $uom Jednotky.
     * @return double Vypoctena vzdalenost. 
     */
    private function calculateDistanceFromLatLong($point1, $point2, $uom='km') {

        switch (strtolower($uom)) {
            case 'km' :
                $earthMeanRadius = 6371.009; // km
                break;
            case 'm' :
                $earthMeanRadius = 6371.009 * 1000; // km
                break;
            case 'miles' :
                $earthMeanRadius = 3958.761; // miles
                break;
            case 'yards' :
            case 'yds' :
                $earthMeanRadius = 3958.761 * 1760; // miles
                break;
            case 'feet' :
            case 'ft' :
                $earthMeanRadius = 3958.761 * 1760 * 3; // miles
                break;
            case 'nm' :
                $earthMeanRadius = 3440.069; // miles
                break;
        }
        $deltaLatitude = deg2rad($point2['latitude'] - $point1['latitude']);
        $deltaLongitude = deg2rad($point2['longitude'] - $point1['longitude']);
        $a = sin($deltaLatitude / 2) * sin($deltaLatitude / 2) +
                cos(deg2rad($point1['latitude'])) * cos(deg2rad($point2['latitude'])) *
                sin($deltaLongitude / 2) * sin($deltaLongitude / 2);
        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
        $distance = $earthMeanRadius * $c;
        return $distance;
    }

    /**
     * Vypocet vzdalenosti dni.
     * 
     * @param type $iStartDate
     * @param type $iEndDate
     * @return int Pocet dni mezi daty 
     */
    private function dateDiff($iStartDate, $iEndDate) {
        return sprintf('%d', (strtotime($iEndDate) - strtotime($iStartDate)) / 86400);
    }

}

?>
